// This File is part of the "nyaqt" Project
// 
// Copyright  2009 sometmes@gmail.com
// All rights reserved
// 
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.InteropServices;
using System.Globalization;

namespace SmAddin
{
    static class ROTHelper
    {
        /// <summary>
        /// Returns the VS instance with the process id found in the 
        /// argument process
        /// </summary>
        public static EnvDTE._DTE GetVisualStudioInstance(string version, int process)
        {
            EnvDTE._DTE foundInstance = null;
            IRunningObjectTable localRot = null;

            // Get the ROT
            try
            {
                if (NativeMethods.Succeeded((int)NativeMethods.GetRunningObjectTable(0, out localRot)))
                {
                    IEnumMoniker enumerator;
                    IMoniker[] monikers = new IMoniker[1];
                    IntPtr fetched = IntPtr.Zero;

                    localRot.EnumRunning(out enumerator);
                    enumerator.Reset();
                    while (enumerator.Next(1, monikers, fetched) == 0)
                    {
                        IBindCtx context;
                        object rotObject;

                        NativeMethods.CreateBindCtx(0, out context);

                        string displayName;
                        monikers[0].GetDisplayName(context, null, out displayName);
                        Marshal.ReleaseComObject(context);

                        if (displayName.StartsWith("!VisualStudio.DTE." + version, StringComparison.Ordinal) &&
                            displayName.EndsWith(":" + process.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal))
                        {
                            localRot.GetObject(monikers[0], out rotObject);

                            foundInstance = rotObject as EnvDTE._DTE;
                            break;
                        }
                    }
                }
            }
            finally
            {
                if (localRot != null)
                {
                    Marshal.ReleaseComObject(localRot);
                }
            }

            return foundInstance;
        }

        public static int CountVisualStudioInstance(string version)
        {
            int count = 0;
            IRunningObjectTable localRot = null;

            // Get the ROT
            try
            {
                if (NativeMethods.Succeeded((int)NativeMethods.GetRunningObjectTable(0, out localRot)))
                {
                    IEnumMoniker enumerator;
                    IMoniker[] monikers = new IMoniker[1];
                    IntPtr fetched = IntPtr.Zero;

                    localRot.EnumRunning(out enumerator);
                    enumerator.Reset();
                    while (enumerator.Next(1, monikers, fetched) == 0)
                    {
                        IBindCtx context;
                        object rotObject;

                        NativeMethods.CreateBindCtx(0, out context);

                        string displayName;
                        monikers[0].GetDisplayName(context, null, out displayName);
                        Marshal.ReleaseComObject(context);

                        if (displayName.StartsWith("!VisualStudio.DTE." + version, StringComparison.Ordinal))
                        {
                            count++;
                        }
                    }
                }
            }
            finally
            {
                if (localRot != null)
                {
                    Marshal.ReleaseComObject(localRot);
                }
            }

            return count;
        }
    }

    static class NativeMethods
    {
        [DllImport("ole32.dll")]
        public static extern uint GetRunningObjectTable(uint reserved, out IRunningObjectTable rot);

        [DllImport("ole32.dll")]
        public static extern uint CreateBindCtx(uint reserved, out IBindCtx ctx);

        internal static bool Succeeded(int Status)
        {
            return (Status >= 0);
        }

        internal static bool Failed(int Status)
        {
            return (Status < 0);
        }
    }
}
